arm: don't use atomic operations to gate non-boot CPUs
authorTim Deegan <tim@xen.org>
Thu, 15 Mar 2012 15:20:37 +0000 (15:20 +0000)
committerTim Deegan <tim@xen.org>
Thu, 15 Mar 2012 15:20:37 +0000 (15:20 +0000)
Since the cache is not enabled that early, better not to rely on
load-linked/store-conditional.  Instead, have the boot CPU call the
other CPUs by their IDs, just like we do later for proper CPU bringup.

Signed-off-by: Tim Deegan <tim@xen.org>
Committed-by: Ian Campbell <ian.campbell@citrix.com>
xen/arch/arm/head.S
xen/arch/arm/setup.c
xen/arch/arm/smpboot.c
xen/include/asm-arm/smp.h

index 4b05bcd82069c3c35479842445b30fc39494ae8c..eabb403dfc38c8661fb582485323c2d744993d6a 100644 (file)
@@ -71,16 +71,13 @@ start:
        bics  r12, r0, #(0xff << 24) /* Mask out flags to get CPU ID */
        beq   boot_cpu               /* If we're CPU 0, boot now */
 
-       /* Non-boot CPUs wait here to be woken up one at a time.
-        * This is basically an open-coded spin-lock to serialize. */
-       ldr   r0, =boot_gate         /* VA of gate */
-       add   r0, r0, r10            /* PA of gate */
-       mov   r1, #1                 /* (1 == locked) */
+       /* Non-boot CPUs wait here to be woken up one at a time. */
 1:     wfe
-       ldrex r2, [r0]               /* Linked read of current value */
-       teq   r2, #0                 /* (0 == unlocked) */
-       strexeq r2, r1, [r0]         /* Matching update -> locked */
-       teq   r2, #0                 /* (0 == succeeded) */
+       dsb
+       ldr   r0, =smp_up_cpu        /* VA of gate */
+       add   r0, r0, r10            /* PA of gate */
+       ldr   r1, [r0]               /* Which CPU is being booted? */
+       teq   r1, r12                /* Is it us? */
        bne   1b
 
 boot_cpu:
@@ -270,16 +267,7 @@ paging:
        teq   r12, #0
        beq   launch
 
-       /* Signal the next non-boot CPU to come and join us here */
-       ldr   r0, =boot_gate         /* VA of gate */
-       add   r0, r0, r10            /* PA of gate */
-       mov   r1, #0                 /* (0 == unlocked) */
-       str   r1, [r0]
-       dsb
-       isb
-       sev
-
-       /* Move on to the relocated pagetables */
+       /* Non-boot CPUs need to move on to the relocated pagetables */
        mov   r0, #0
        ldr   r4, =boot_httbr        /* VA of HTTBR value stashed by CPU 0 */
        add   r4, r4, r10            /* PA of it */
index fa994c3f86768d30527f447014dcf480ca4587a5..777f8fb144c9c471b1490a0044ede6cd8dbd7ff7 100644 (file)
 #include <asm/setup.h>
 #include "gic.h"
 
-/* Spinlock for serializing CPU bringup */
-unsigned long __initdata boot_gate = 1;
-/* Number of non-boot CPUs ready to enter C */
-unsigned long __initdata ready_cpus = 0;
-
 static __attribute_used__ void init_done(void)
 {
     free_init_memory();
@@ -152,8 +147,6 @@ void __init start_xen(unsigned long boot_phys_offset,
     void *fdt;
     size_t fdt_size;
     int cpus, i;
-    paddr_t gate_pa;
-    unsigned long *gate;
 
     fdt = (void *)BOOT_MISC_VIRT_START
         + (atag_paddr & ((1 << SECOND_SHIFT) - 1));
@@ -169,25 +162,11 @@ void __init start_xen(unsigned long boot_phys_offset,
     console_init_preirq();
 #endif
 
-    percpu_init_areas();
-    set_processor_id(0); /* needed early, for smp_processor_id() */
-
     cpus = gic_init();
+    make_cpus_ready(cpus, boot_phys_offset);
 
-    printk("Waiting for %i other CPUs to be ready\n", cpus - 1);
-    /* Bring the other CPUs up to paging before the original
-     * copy of .text gets overwritten.  We need to use the unrelocated
-     * copy of boot_gate as that's the one the others can see. */ 
-    gate_pa = ((unsigned long) &boot_gate) + boot_phys_offset;
-    gate = map_domain_page(gate_pa >> PAGE_SHIFT) + (gate_pa & ~PAGE_MASK); 
-    *gate = 0;
-    unmap_domain_page(gate);
-    /* Now send an event to wake the first non-boot CPU */
-    asm volatile("dsb; isb; sev");
-    /* And wait for them all to be ready. */
-    while ( ready_cpus + 1 < cpus )
-        smp_rmb();
-
+    percpu_init_areas();
+    set_processor_id(0); /* needed early, for smp_processor_id() */
     __set_current((struct vcpu *)0xfffff000); /* debug sanity */
     idle_vcpu[0] = current;
 
index f369d70f4cae2a1e1b03e5f184b0dffbb9fa2a56..4288710c97f8c5fcbf89ad2870c11ffae0ce5a78 100644 (file)
@@ -19,6 +19,7 @@
 #include <xen/cpu.h>
 #include <xen/cpumask.h>
 #include <xen/delay.h>
+#include <xen/domain_page.h>
 #include <xen/errno.h>
 #include <xen/init.h>
 #include <xen/mm.h>
@@ -41,11 +42,17 @@ static unsigned char __initdata cpu0_boot_stack[STACK_SIZE]
 /* Pointer to the stack, used by head.S when entering C */
 unsigned char *init_stack = cpu0_boot_stack;
 
+/* Shared state for coordinating CPU bringup */
+unsigned long smp_up_cpu = 0;
+static bool_t cpu_is_dead = 0;
+
+/* Number of non-boot CPUs ready to enter C */
+unsigned long __initdata ready_cpus = 0;
+
 void __init
 smp_prepare_cpus (unsigned int max_cpus)
 {
     int i;
-    set_processor_id(0); /* needed early, for smp_processor_id() */
 
     cpumask_clear(&cpu_online_map);
     cpumask_set_cpu(0, &cpu_online_map);
@@ -56,9 +63,30 @@ smp_prepare_cpus (unsigned int max_cpus)
     cpumask_copy(&cpu_present_map, &cpu_possible_map);
 }
 
-/* Shared state for coordinating CPU bringup */
-unsigned long smp_up_cpu = 0;
-static bool_t cpu_is_dead = 0;
+void __init
+make_cpus_ready(unsigned int max_cpus, unsigned long boot_phys_offset)
+{
+    unsigned long *gate;
+    paddr_t gate_pa;
+    int i;
+
+    printk("Waiting for %i other CPUs to be ready\n", max_cpus - 1);
+    /* We use the unrelocated copy of smp_up_cpu as that's the one the
+     * others can see. */ 
+    gate_pa = ((paddr_t) (unsigned long) &smp_up_cpu) + boot_phys_offset;
+    gate = map_domain_page(gate_pa >> PAGE_SHIFT) + (gate_pa & ~PAGE_MASK); 
+    for ( i = 1; i < max_cpus; i++ )
+    {
+        /* Tell the next CPU to get ready */
+        /* TODO: handle boards where CPUIDs are not contiguous */
+        *gate = i;
+        asm volatile("dsb; isb; sev");
+        /* And wait for it to respond */
+        while ( ready_cpus < i )
+            smp_rmb();
+    }
+    unmap_domain_page(gate);
+}
 
 /* Boot the current CPU */
 void __cpuinit start_secondary(unsigned long boot_phys_offset,
index 8099f6da4fdbbf53d554355cfed8537203f69781..a98032dc2c1afabc27f7143f9f14821c516cf6f4 100644 (file)
@@ -16,6 +16,12 @@ DECLARE_PER_CPU(cpumask_var_t, cpu_core_mask);
 
 extern void stop_cpu(void);
 
+/* Bring the non-boot CPUs up to paging and ready to enter C.  
+ * Must be called after Xen is relocated but before the original copy of
+ * .text gets overwritten. */
+extern void
+make_cpus_ready(unsigned int max_cpus, unsigned long boot_phys_offset);
+
 #endif
 /*
  * Local variables: